﻿/*	Copyright (c) 2017 Jean-Marc VIGLINO, 
  released under the CeCILL-B license (French BSD license)
  (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/

import ol_ext_inherits from '../util/ext'
import ol_style_Style from 'ol/style/Style'
import ol_style_Stroke from 'ol/style/Stroke'
import ol_style_Fill from 'ol/style/Fill'
import ol_style_Text from 'ol/style/Text'
import {boundingExtent as ol_extent_boundingExtent} from 'ol/extent'
import ol_control_CanvasBase from './CanvasBase'

/**
 * Draw a grid reference on the map and add an index.
 *
 * @constructor
 * @extends {ol_control_CanvasBase}
 * @fires select
 * @param {Object=} Control options.
 *  @param {ol_style_Style} options.style Style to use for drawing the grid (stroke and text), default black.
 *  @param {number} options.maxResolution max resolution to display the graticule
 *  @param {ol.extent} options.extent extent of the grid, required
 *  @param {ol.size} options.size number of lines and cols, required
 *  @param {number} options.margin margin to display text (in px), default 0px
 *  @param {ol.source.Vector} options.source source to use for the index, default none (use setIndex to reset the index)
 *  @param {string | function} options.property a property to display in the index or a function that takes a feature and return the name to display in the index, default 'name'.
 *  @param {function|undefined} options.sortFeatures sort function to sort 2 features in the index, default sort on property option
 *  @param {function|undefined} options.indexTitle a function that takes a feature and return the title to display in the index, default the first letter of property option
 *  @param {string} options.filterLabel label to display in the search bar, default 'filter'
 */
var ol_control_GridReference = function(options) {
  if (!options) options = {};

  // Initialize parent
  var elt = document.createElement("div");
  elt.className = (!options.target ? "ol-control ":"") +"ol-gridreference ol-unselectable "+(options.className||"");

  options.style = options.style || new ol_style_Style({
    stroke: new ol_style_Stroke({ color:"#000", width:1 }),
    text: new ol_style_Text({
      font: "bold 14px Arial",
      stroke: new ol_style_Stroke({ color:"#fff", width:2 }),
      fill: new ol_style_Fill({ color:"#000" }),
    })
  });

  ol_control_CanvasBase.call(this, {
    element: elt,
    target: options.target,
    style: options.style
  });

  if (typeof (options.property)=='function') this.getFeatureName = options.property;
  if (typeof (options.sortFeatures)=='function') this.sortFeatures = options.sortFeatures;
  if (typeof (options.indexTitle)=='function') this.indexTitle = options.indexTitle;

  // Set index using the source
  this.source_ = options.source;
  if (options.source) {
    this.setIndex(options.source.getFeatures(), options);
    // reload on ready
    options.source.once('change',function() {
      if (options.source.getState() === 'ready'){
        this.setIndex(options.source.getFeatures(), options);
      }
    }.bind(this));
  }

  // Options
  this.set('maxResolution', options.maxResolution || Infinity);
  this.set('extent', options.extent);
  this.set('size', options.size);
  this.set('margin', options.margin || 0);
  this.set('property', options.property || 'name');
  this.set('filterLabel', options.filterLabel || 'filter');
};
ol_ext_inherits(ol_control_GridReference, ol_control_CanvasBase);

/** Returns the text to be displayed in the index
 * @param {ol.Feature} f the feature
 * @return {string} the text to be displayed in the index
 * @api
 */
ol_control_GridReference.prototype.getFeatureName = function (f) {
  return f.get(this.get('property')||'name');
};

/** Sort function
 * @param {ol.Feature} a first feature
 * @param {ol.Feature} b second feature
 * @return {Number} 0 if a==b, -1 if a<b, 1 if a>b
 * @api
 */
ol_control_GridReference.prototype.sortFeatures = function (a,b) {
  return (this.getFeatureName(a) == this.getFeatureName(b)) ? 0 : (this.getFeatureName(a) < this.getFeatureName(b)) ? -1 : 1; 
};

/** Get the feature title
 * @param {ol.Feature} f
 * @return the first letter of the eature name (getFeatureName)
 * @api
 */
ol_control_GridReference.prototype.indexTitle = function (f) {
  return this.getFeatureName(f).charAt(0);
};

/** Display features in the index
 * @param { Array<ol.Feature> | ol.Collection<ol.Feature> } features
 */
ol_control_GridReference.prototype.setIndex = function (features) {
  if (!this.getMap()) return;
  var self = this;
  if (features.getArray) features = features.getArray();
  features.sort ( function(a,b) { return self.sortFeatures(a,b); } );
  this.element.innerHTML = "";
  var elt = this.element;

  var search = document.createElement("input");
  search.setAttribute('type', 'search');
  search.setAttribute('placeholder', this.get('filterLabel') || 'filter');
  var searchKeyupFunction = function() {
    var v = this.value.replace(/^\*/,'');
    // console.log(v)
    var r = new RegExp (v, 'i');
    ul.querySelectorAll('li').forEach(function(li) {
      if (li.classList.contains('ol-title')) {
        li.style.display = '';
      } else {
        if (r.test(li.querySelector('.ol-name').textContent)) li.style.display = '';
        else li.style.display = 'none';
      }
    });
    ul.querySelectorAll("li.ol-title").forEach(function(li) {
      var nextAll = false;
      nextAll = [].filter.call(li.parentNode.children, function (htmlElement) {
        return (htmlElement.previousElementSibling === li) ? nextAll = true : nextAll;
      });
      console.log(nextAll);
      var nextVisible = nextAll[0];
      if (nextVisible.length && !nextVisible.classList.contains('ol-title')) li.style.display = '';
      else li.style.display = 'none';
    });
  };
  search.addEventListener('search', searchKeyupFunction);
  search.addEventListener('keyup', searchKeyupFunction);
  elt.appendChild(search);

  var ul = document.createElement("ul");
  elt.appendChild(ul);
  var r, title;
  for (var i=0, f; f=features[i]; i++) {
    (function(feat) {
      r = self.getReference(feat.getGeometry().getFirstCoordinate());
      if (r) {
        var name = self.getFeatureName(feat);
        var c = self.indexTitle(feat);
        if (c != title) {
          var li_title = document.createElement("li");
          li_title.classList.add('ol-title');
          li_title.textContent = c;
          ul.appendChild(li_title);
        }
        title = c;
        var li_ref_name = document.createElement("li");
        var span_name = document.createElement("span");
            span_name.classList.add("ol-name");
            span_name.textContent = name;
        li_ref_name.appendChild(span_name);
        var span_ref = document.createElement("span");
            span_ref.classList.add("ol-ref");
            span_ref.textContent = r;
        li_ref_name.appendChild(span_ref);
        var feature = feat;
        li_ref_name.addEventListener("click", function() {
          self.dispatchEvent({ type:"select", feature: feature });
        });

        ul.appendChild(li_ref_name);
      }
    })(f);
  }
};

/** Get reference for a coord
*	@param {ol.coordinate} coords
*	@return {string} the reference
*/
ol_control_GridReference.prototype.getReference = function (coords) {
  if (!this.getMap()) return;
  var extent = this.get('extent');
  var size = this.get('size');

  var dx = Math.floor ( (coords[0] - extent[0]) / (extent[2]- extent[0]) * size[0] );
  if (dx<0 || dx>=size[0]) return "";
  var dy = Math.floor ( (extent[3] - coords[1]) / (extent[3]- extent[1]) * size[1] );
  if (dy<0 || dy>=size[1]) return "";
  return String.fromCharCode(65+dx)+dy;
};

/** Draw the grid
* @param {ol.event} e postcompose event
* @private
*/
ol_control_GridReference.prototype._draw = function (e) {
  if (this.get('maxResolution')<e.frameState.viewState.resolution) return;

  var ctx = this.getContext(e);
  var canvas = ctx.canvas;
  var ratio = e.frameState.pixelRatio;

  var w = canvas.width/ratio;
  var h = canvas.height/ratio;

  var extent = this.get('extent');
  var size = this.get('size');

  var map = this.getMap();
  var ex = ol_extent_boundingExtent([map.getPixelFromCoordinate([extent[0],extent[1]]), map.getPixelFromCoordinate([extent[2],extent[3]])]);
  var p0 = [ex[0],ex[1]];
  var p1 = [ex[2],ex[3]];
  var dx = (p1[0]-p0[0])/size[0];
  var dy = (p1[1]-p0[1])/size[1];

  ctx.save();
    var margin = this.get('margin');
    ctx.scale(ratio,ratio);

    ctx.strokeStyle = this.getStroke().getColor();
    ctx.lineWidth = this.getStroke().getWidth();

    // Draw grid
    ctx.beginPath();
    var i;
    for (i=0; i<=size[0]; i++) {
      ctx.moveTo(p0[0]+i*dx, p0[1]);
      ctx.lineTo(p0[0]+i*dx, p1[1]);
    }
    for (i=0; i<=size[1]; i++) {
      ctx.moveTo(p0[0], p0[1]+i*dy);
      ctx.lineTo(p1[0], p0[1]+i*dy);
    }
    ctx.stroke();

    // Draw text
    ctx.font = this.getTextFont();
    ctx.fillStyle = this.getTextFill().getColor();
    ctx.strokeStyle = this.getTextStroke().getColor();
    var lw = ctx.lineWidth = this.getTextStroke().getWidth();
    var spacing = margin +lw;
    ctx.textAlign = 'center';
    var letter, x, y;
    for (i=0; i<size[0]; i++) {
      letter = String.fromCharCode(65+i);
      x = p0[0]+i*dx+dx/2;
      y = p0[1]-spacing;
      if (y<0) {
        y = spacing;
        ctx.textBaseline = 'hanging';
      }
      else ctx.textBaseline = 'alphabetic';
      ctx.strokeText(letter, x, y);
      ctx.fillText(letter, x, y);
      y = p1[1]+spacing;
      if (y>h) {
        y = h-spacing;
        ctx.textBaseline = 'alphabetic';
      }
      else ctx.textBaseline = 'hanging';
      ctx.strokeText(letter, x, y);
      ctx.fillText(letter, x, y);
    }
    ctx.textBaseline = 'middle';
    for (i=0; i<size[1]; i++) {
      y = p0[1]+i*dy+dy/2;
      ctx.textAlign = 'right';
      x = p0[0] - spacing;
      if (x<0) {
        x = spacing;
        ctx.textAlign = 'left';
      }
      else ctx.textAlign = 'right';
      ctx.strokeText(i, x, y);
      ctx.fillText(i, x, y);
      x = p1[0] + spacing;
      if (x>w) {
        x = w-spacing;
        ctx.textAlign = 'right';
      }
      else ctx.textAlign = 'left';
      ctx.strokeText(i, x, y);
      ctx.fillText(i, x, y);
    }

  ctx.restore();
};

export default ol_control_GridReference
